﻿using CsvHelper;
using NetOffice.ExcelApi;
using NetOffice.ExcelApi.Enums;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Documents;
using Excel = NetOffice.ExcelApi;

namespace ProcmonPivot
{
	class RowWriter
	{
		private Excel.Range range;

		public RowWriter(Excel.Range range)
		{
			this.range = range;
		}


		public void Write(object value)
		{
			range.Value = value;
			Count++;
			var sheet = range.Worksheet;
			range = sheet.Cells[range.Row, range.Column + 1];
		}

		public int Count
		{
			get;
			private set;
		}
	}

	class PivotBuilder
	{
		private Excel.PivotTable pivotTable;

		Dictionary<XlPivotFieldOrientation, int> _FieldsCount = new Dictionary<XlPivotFieldOrientation, int>();
		public PivotBuilder(Excel.PivotTable pivotTable)
		{
			this.pivotTable = pivotTable;
			InitCount();
		}

		private void InitCount()
		{
			foreach(var val in Enum.GetValues(typeof(XlPivotFieldOrientation)))
			{
				_FieldsCount[(XlPivotFieldOrientation)val] = 1;
			}
		}

		public Excel.PivotField LastField
		{
			get;
			private set;
		}

		public void Add(string fieldName, XlPivotFieldOrientation xlPivotFieldOrientation)
		{
			var count = _FieldsCount[xlPivotFieldOrientation];
			var field = (Excel.PivotField)pivotTable.PivotFields(fieldName);
			field.Orientation = xlPivotFieldOrientation;
			field.Position = count;

			var beforeLast = LastField;
			LastField = field;
			try
			{
				var total = (Array)field.Subtotals;
				for(int i = 1 ; i < total.Length + 1 ; i++)
				{
					total.SetValue(false, i);
				}
				field.Subtotals = total;
				beforeLast.ShowDetail = false;
			}
			catch
			{
			}
			_FieldsCount[xlPivotFieldOrientation] = count + 1;
		}
	}
	class ProcmonPivotOffice
	{

		public static void Create(string fileName)
		{
			using(var fs = File.Open(fileName, FileMode.Open))
			{
				var reader = new CsvReader(new StreamReader(fs), new CsvHelper.Configuration.CsvConfiguration()
				{
					Delimiter = ",",
					HasHeaderRecord = true,
				});

				Create(reader);
			}
		}

		private static void Create(CsvReader reader)
		{
			List<List<Object>> dataRowList = new List<List<object>>();
			using(var app = new Excel.Application())
			{
				app.Visible = true;
				var book = app.Workbooks.Add();
				var data = (Excel.Worksheet)book.Worksheets[1];

				var header = new RowWriter(data.Cells[1, 1]);


				int maxPaths = 0;

				while(reader.Read())
				{
					var row = new List<object>();
					foreach(var fieldHeader in reader.FieldHeaders)
					{
						row.Add(reader.GetField(fieldHeader));
					}
					row.Add(ParseCategory(reader.GetField("Operation")));
					var path = reader.GetField("Path");
					var paths = path.Split('\\');

					maxPaths = Math.Max(maxPaths, paths.Length);
					for(int i = 0 ; i < paths.Length ; i++)
					{
						row.Add(paths[i]);
					}
					dataRowList.Add(row);
				}


				WriteHeaders(header, reader);
				for(int i = 0 ; i < maxPaths ; i++)
					header.Write("Path" + i);

				if(dataRowList.Count > 0)
				{
					object[,] array = ToArray(dataRowList, header.Count);

					var end = data.Cells[1 + dataRowList.Count, header.Count];
					var range = data.Range(data.Cells[2, 1], end);
					range.Value = array;

					range = data.Range(data.Cells[1, 1], end);

					var pivotSheet = (Excel.Worksheet)book.Worksheets[2];
					pivotSheet.Activate();


					data.Names.Add("DataRange", range); //Need to do that for big data (more than 2^16 row)
					var pivot = book
						.PivotCaches()
						.Create(XlPivotTableSourceType.xlDatabase, "DataRange")
						.CreatePivotTable(pivotSheet.Cells[1, 1]);

					if(Preferences.Default.CreatePathHierarchy)
					{
						PivotBuilder builder = new PivotBuilder(pivot);
						for(int i = 0 ; i < maxPaths ; i++)
						{
							builder.Add("Path" + i, XlPivotFieldOrientation.xlRowField);
						}
						 pivot.AddDataField(pivot.PivotFields(1), "Count", XlConsolidationFunction.xlCount);
					}
				}
			}
		}

		private static object[,] ToArray(List<List<object>> dataRowList, int headerCount)
		{
			var array = new Object[dataRowList.Count, headerCount];
			int x = 0;
			foreach(var row in dataRowList)
			{
				int y = 0;
				foreach(var cell in row)
				{
					array[x, y] = cell;
					y++;
				}
				x++;
			}
			return array;
		}

		private static void WriteHeaders(RowWriter header, CsvReader reader)
		{
			foreach(var head in reader.FieldHeaders)
			{
				header.Write(head);
			}
			header.Write("Category");
		}

		public static string ParseCategory(string operation)
		{
			if(operation.StartsWith("Reg"))
				return "Registry";
			if(operation.Contains("File"))
				return "File";
			return "Other";
		}
	}
}
